function createTextNode(text) {
  return {
    type: 'TEXT_ELEMENT',
    props: {
      nodeValue: text,
      children: []
    }
  }
}

function createElement(type, props, ...children) {
  return {
    type,
    props: {
      ...props,
      children: children.map(child => {
        // console.log(child);
        const isTextNode = typeof child === 'string' || typeof child === 'number'
        return isTextNode ? createTextNode(child) : child
      })
    }
  }
}

function createDom(type) {
  return type === 'TEXT_ELEMENT' ? document.createTextNode("") : document.createElement(type)
}

function updateProps(dom, nextProps, prevProps) {
  /**
   * 此处有三种情况
   * 1. old 有 new 没有： 删除
   * 2. new 有 old 没有： 新增
   * 3. new 有 old 有 ： 更新 
   */
  //  1. old 有 new 没有： 删除
  Object.keys(prevProps).forEach((key) => {
    if (key !== 'children') {
      // old属性在新节点中没有则需要删除
      if (!(key in prevProps)) {
        dom.removeAttribute(key)
      }
    }
  })
  // 2和3
  Object.keys(nextProps).forEach((key) => {
    if (key !== 'children') {
      if (nextProps[key] !== prevProps[key]) {
        if (key.startsWith('on')) {
          const eventType = key.slice('2').toLowerCase()
          dom.removeEventListener(eventType, prevProps[key])
          dom.addEventListener(eventType, nextProps[key])
        } else {
          dom[key] = nextProps[key]
        }
      }
    }
  })
}
// reconcile 使调和一致
function reconcileChildren(fiber, children) {
  // 这里直接找上次的子元素，然后到循环里后续需要把oldFiber依次找兄弟节点
  let oldFiber = fiber.alternate?.child // child是之前的虚拟dom
  let prevChild = null
  children.forEach((child, index) => {
    const isSameType = oldFiber && oldFiber.type === child.type
    // 对比当前节点类型和oldFiber类型 一样的话是更新，不一样则放行创建后续处理（现在只处理props的更新)
    let newFiber;
    if (isSameType) {
      newFiber = {
        type: child.type,
        props: child.props,
        child: null,
        parent: fiber,
        sibling: null,
        dom: oldFiber.dom,
        effectTag: 'update',
        alternate: oldFiber
      }
    } else {
      if (child) {
        newFiber = {
          type: child.type,
          props: child.props,
          child: null,
          parent: fiber,
          sibling: null,
          dom: null,
          effectTag: 'placement'  // 放行
        }
      }
      // 这里能找到就说明是update更新的，而不是初始化render
      if (oldFiber) {
        deletions.push(oldFiber)
      }
    }
    // 直接把指针给到fiber的兄弟
    if (oldFiber) {
      oldFiber = oldFiber.sibling
    }
    if (index === 0) {
      fiber.child = newFiber
    } else {
      prevChild.sibling = newFiber
    }
    if (newFiber) {
      prevChild = newFiber
    }
  })
  // 如果多个兄弟节点则需要把所有兄弟节点都删掉
  while (oldFiber) {
    deletions.push(oldFiber)
    oldFiber = oldFiber.sibling
  }
}
function updateFunctionComponent(fiber) {
  stateHooks = []
  stateHooksIndex = 0
  effectHooks = []
  // 每次要更新的是哪个就记录
  wipFiber = fiber
  const children = [fiber.type(fiber.props)]
  reconcileChildren(fiber, children)
}

function updateHostComponent(fiber) {
  if (!fiber.dom) {
    const dom = fiber.dom = createDom(fiber.type)
    updateProps(dom, fiber.props, {})
  }
  const children = fiber.props.children
  reconcileChildren(fiber, children)
}

function performWorkOfUnit(fiber) {
  const isFunctionComponent = typeof fiber.type === 'function'
  if (isFunctionComponent) {
    updateFunctionComponent(fiber)
  } else {
    updateHostComponent(fiber)
  }

  if (fiber.child) {
    return fiber.child
  }
  // 如果父级节点没有兄弟，就找到父节点的父级一直网上找 的兄弟节点
  let nextFiber = fiber
  while (nextFiber) {
    if (nextFiber.sibling) {
      return nextFiber.sibling
    }
    nextFiber = nextFiber.parent
  }

}
function workLoop(deadline) {
  let shouldYield = false;
  while (!shouldYield && nextUnitOfWork) {
    nextUnitOfWork = performWorkOfUnit(nextUnitOfWork)
    // 当前fiber节点
    if (wipRoot?.sibling?.type === nextUnitOfWork?.type) {
      nextUnitOfWork = undefined
    }
    if (!nextUnitOfWork && wipRoot) {
      commitRoot()
    }
    shouldYield = deadline.timeRemaining() < 1
  }
  requestIdleCallback(workLoop)

}


function commitRoot() {
  deletions.forEach(commitDeletion)
  commitWork(wipRoot.child)
  commitEffectHooks()
  currentRoot = wipRoot
  wipRoot = null
  deletions = []
}

function commitEffectHooks() {
  function run(fiber) {
    if (!fiber) return
    if (!fiber.alternate) { // 是初始化则执行callback
      fiber.effectHooks?.forEach(hook => {
        hook.cleanup = hook.callback()
      })
    } else { // 是依赖更新的则比对是否改变，确定是否执行callback
      fiber.effectHooks?.forEach((newHook, index) => {
        if (newHook.deps.length > 0) {
          const oldEffectHook = fiber.alternate?.effectHooks[index]
          // some 
          const needUpdate = oldEffectHook?.deps?.some((oldDep, i) => {
            return oldDep !== newHook[i]
          })
          needUpdate && (newHook.cleanup = newHook.callback())
        }
      })
    }

    run(fiber.child)
    run(fiber.sibling)
  }

  function runCleanup(fiber) {
    if (!fiber) return
    fiber.alternate?.effectHooks?.forEach(hook => {
      if (hook.deps.length > 0) {
        hook.cleanup && hook.cleanup()
      }
    })
    runCleanup(fiber.child)
    runCleanup(fiber.sibling)
  }
  runCleanup(wipRoot)
  run(wipRoot)
}
function commitDeletion(fiber) {
  if (fiber.dom) {
    // 如果是FC到这一步的时候 fiber.parent也就是FC就没有dom所以需要再次往上找
    let parentFiber = fiber.parent
    while (!parentFiber.dom) {
      parentFiber = parentFiber.parent
    }
    parentFiber.dom.removeChild(fiber.dom)
  } else {
    // FC组件的child是真实dom
    commitDeletion(fiber.child)
  }
}
// 递归调用
function commitWork(fiber) {
  if (!fiber) return
  // 由于FC没有dom，所以需要找到最近的有dom的父级
  let parentFiber = fiber.parent
  while (!parentFiber.dom) {
    parentFiber = parentFiber.parent
  }
  // 如果有dom，就添加到父级dom上，不然会添加一个null
  if (fiber.effectTag === 'update') {
    updateProps(fiber.dom, fiber.props, fiber.alternate?.props)
  } else if (fiber.effectTag === 'placement') {
    if (fiber.dom) {
      parentFiber.dom.append(fiber.dom)
    }
  }
  commitWork(fiber.child)
  commitWork(fiber.sibling)
}

requestIdleCallback(workLoop)

// 储存根节点
let wipRoot = null
let currentRoot = null
let wipFiber = null

// 添加任务调度器
let nextUnitOfWork = null
// 统一记录要删除的fiber
let deletions = []

function render(el, container) {
  wipRoot = {
    dom: container,
    props: {
      children: [el]
    }
  }
  nextUnitOfWork = wipRoot
}
function update() {
  let currentFiber = wipFiber
  return () => {
    // 每次要更新的是哪个就记录
    console.log(currentFiber);
    wipRoot = {
      ...currentFiber,
      alternate: currentFiber
    }
    nextUnitOfWork = wipRoot
  }
}

let stateHooks;
let stateHooksIndex;
// 走dom渲染的时候会走useState
function useState(initial) {
  // 记录当前组件fiber的值供setState使用
  let currentFiber = wipFiber
  const oldHook = currentFiber.alternate?.stateHooks[stateHooksIndex]
  const stateHook = {
    state: oldHook ? oldHook.state : initial,
    queue: oldHook ? oldHook.queue : []
  }
  stateHook.queue.forEach((action) => {
    stateHook.state = action(stateHook.state)
  })
  // 消费完后清空
  stateHook.queue = []

  stateHooks.push(stateHook)
  stateHooksIndex++

  currentFiber.stateHooks = stateHooks
  function setState(action) {
    const eagerState = typeof action === 'function' ? action(stateHook.state) : action
    if (eagerState === stateHook.state) return
    // 每次更新添加到队列
    stateHook.queue.push(typeof action === 'function' ? action : () => action)
    wipRoot = {
      ...currentFiber,
      alternate: currentFiber
    }
    nextUnitOfWork = wipRoot
  }
  return [stateHook.state, setState]
}

let effectHooks;
function useEffect(callback, deps) {
  const effectHook = {
    callback,
    deps,
    cleanup: undefined
  }
  effectHooks.push(effectHook)
  wipFiber.effectHooks = effectHooks
}
const React = {
  useState,
  useEffect,
  update,
  render,
  createElement
}

export default React


